package orthoplanner.com.google.code.tools;

import java.awt.Color;
import java.awt.Point;

import processing.core.PApplet;
import processing.core.PVector;

public class Line implements Drawable {
	private int strokeWeight;
	private Point start, end;
	private Double tolerance;
	private final String name;
	private Color color;
	public Line(Point start, Point end) {
		super();
		this.start = start;
		this.end = end;
		tolerance = 10.0;
		name="Line ";
		strokeWeight=1;
		color=new Color(93, 197, 255);
	}

	public Double getTolerance() {
		return tolerance;
	}

	public void setTolerance(Double tolerance) {
		this.tolerance = tolerance;
	}

	public void setStart(Point start) {
		this.start = start;
	}

	public void setEnd(Point end) {
		this.end = end;
	}

	public PVector toVector() {
		return new PVector(end.x - start.x, end.y - start.y);
	}
	public double getMagnitude()
	{
		//return Math.sqrt(Math.pow(end.x-start.x,2)+Math.pow(end.y-start.y,2));
		return new PVector(end.x - start.x, end.y - start.y).mag();
	}

	public Point getStart() {
		return start;
	}

	public Point getEnd() {
		return end;
	}

	public double getGradient() {
		return (end.y - start.y * 1.00) / (end.x - start.x);
	}

	public double getYintercept() {
		return start.y - (getGradient() * start.x);
	}

	public boolean isUpon(Point p) {
		return distance(p) < tolerance && p.x > Math.min(start.x, end.x)
				&& p.x < Math.max(start.x, end.x)
				&& p.y > Math.min(start.y, end.y)
				&& p.y < Math.max(start.y, end.y);
	}

	public double distance(Point p) {
		double dist;
		if (!Double.isInfinite(getGradient())) {
			dist = Math.abs(getGradient() * p.x - p.y + getYintercept())
					/ Math.sqrt(1 + getGradient() * getGradient());
		} else {
			dist = Math.abs(start.x - p.x);
		}
		return dist;
	}

	public Point getNearestPoint(Point p1) {
		Point p2 = new Point();
		double m = -1.0 / getGradient();
		double c = p1.y - m * p1.x;
		p2.x = (int) Math.round((c - this.getYintercept())
				/ (this.getGradient() - m));
		p2.y = (int) Math.round(this.getGradient() * p1.x
				+ this.getYintercept());
		return p2;
	}

	public boolean isUponNode(Point p) {
		return PApplet.dist(p.x, p.y, start.x, start.y) < tolerance
				|| PApplet.dist(p.x, p.y, end.x, end.y) < tolerance;
	}

	public Point nearestEnd(Point p) {
		Point n = null;
		if (PApplet.dist(p.x, p.y, start.x, start.y) < tolerance) {
			n = start;
		} else if (PApplet.dist(p.x, p.y, end.x, end.y) < tolerance) {
			n = end;
		}
		return n;
	}

	public Point getIntersect(Line otherLine) {
		Point p = new Point();
		Line l1, l2;
		if (this.getGradient() > otherLine.getGradient()) {
			l1 = this;
			l2 = otherLine;
		} else {
			l2 = this;
			l1 = otherLine;
		}
		double m1 = l1.getGradient();
		double m2 = l2.getGradient();
		double y1 = l1.getYintercept();
		double y2 = l2.getYintercept();

		p.x = (int) Math.round((y2 - y1) / (m1 - m2));
		p.y = (int) Math.round(m1 * p.x + y1);
		return p;
	}

	public boolean isIntersectable(Line otherLine) {
		return Math.abs(this.getGradient() - otherLine.getGradient()) > 1;
	}

	public Point getPerpendicularPoint(Point p) {
		double m = -1.0 / getGradient();
		double c = p.y - (p.x * m);
		Point intersect = new Point();
		intersect.x = (int) Math.round((c - getYintercept())
				/ (getGradient() - m));
		intersect.y = (int) Math.round(getGradient() * intersect.x
				+ getYintercept());
		return intersect;
	}

	public double getAngle(Line otherLine) {
		PVector v1 = this.toVector();
		PVector v2 = otherLine.toVector();
		v1.normalize();
		v2.normalize();
		float f = v1.dot(v2);

		double tmp = Math.round(Math.acos(f) * 100.0) / 100.0;
		return tmp;
	}

	public void draw(PApplet imgViewer) {
		
		imgViewer.stroke(color.getRed(),color.getGreen(),color.getBlue());
		imgViewer.strokeWeight(strokeWeight);
		imgViewer.line(start.x, start.y, end.x, end.y);
		imgViewer.stroke(color.getRed(),color.getGreen(),color.getBlue());
		imgViewer.fill(color.getRed(),color.getGreen(),color.getBlue());
		imgViewer.ellipse(start.x, start.y, 6, 6);
		imgViewer.ellipse(end.x, end.y, 6, 6);
		imgViewer.noStroke();
		imgViewer.fill(color.getRed(),color.getGreen(),color.getBlue());
		imgViewer.ellipse(start.x + (end.x - start.x)/ 2.F,start.y	+ (end.y - start.y) / 2.F, 4, 4);
		imgViewer.text(Math.round(this.getMagnitude()) ,start.x + (end.x - start.x)/ 2.F, start.y	+ (end.y - start.y) / 2.F);
		
	}

	public Boolean isConfigurable(Point p) {
		
		return this.isUponNode(p);
	}

	public Boolean isMovable(Point p) {
		return this.isUpon(p);	
		}


	public void configure(Point p) {
		if (p.distance(this.getStart()) < p.distance(this.getEnd())) {
			this.setStart(p);
		} else {
			this.setEnd(p);
		}
		
	}

	public void move(int dx, int dy) {
		start.x += dx ;
		start.y += dy;
		end.x += dx;
		end.y += dy;
	}

	public String getName() {
		return name;
	}

	public void setColor(Color color) {
		this.color=color;
	}

	public void setStroke(int value) {
		this.strokeWeight=value;
	}

	public Color getColor() {
		return this.color;
	}

	public int getStrokeWeight() {
		return strokeWeight;
	}
}